/* 
 * File:   GraphicsCore.cpp
 * Author: RedEyedKiller
 * 
 * Created on 20 Ιούλιος 2011, 6:03 πμ
 */

#include "GraphicsCore.h"

#include "../Logger.h"
#include "Canvas.h"
#include "GLColor.h"
#include "Texture.h"
#include "TextureCoords.h"
#include "TextImage.h"
#include "Vertex.h"
#include <SDL/SDL_opengl.h>
#include <list>
#include <algorithm>
#include "../Logger.h"
#include "BlendingMethodManager.h"

namespace sdl
{

GraphicsCore::~GraphicsCore()
{
    if( sdlScreen != NULL )
        delete sdlScreen;
    //delete mainScreen;
}

bool GraphicsCore::Initialize(int screenW, int screenH, int bpp, bool fullscreen, bool useSdlScreen)
{
    SetOpenglConstants();
    
    mainScreen = new Screen( screenW, screenH, bpp, fullscreen, true );

    if( useSdlScreen )
        sdlScreen = new Canvas( screenW, screenH );
    else
        sdlScreen = NULL;

    //The color of sreen after glClear call.
    glClearColor( 0, 0, 0, 0 );

    glViewport( 0, 0, screenW, screenH );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    glOrtho( 0, screenW, screenH, 0, 1, -1 );

    glEnable( GL_TEXTURE_2D );
    
    blengingMethods.Initialize();
    
    CreateExtensionList();

    //intialize shader functionality [future]
    if( !InitializeGLSL( ) )
    {
        LOGERROR << "GLSL could not be initialized!";
    }

    return true;
}

bool GraphicsCore::Render(const gl::Texture& texture, const Math::Rect& clip, const gl::Color& color,
                          const Math::Vector2F& position, const Math::Vector2F& scale,float angle,
                          bool flip[2],gl::BlendingMethodName blend /*= "Alpha"*/)
{
    //revert view matrix
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    
    //load up the specified blending method.
    blengingMethods.EnableMethod(blend);
    
    //create texture coordinates
    gl::TextureCoords textcoords[4];
    TextureCoordinates( textcoords, texture, clip, flip );

    //create the vertex array
    gl::Vertex vertecies[4];
    Transorm( vertecies, position, clip, scale );
	
    if(angle > 0.01) glRotatef(angle,0,0,1);
    
    glTranslated(position.GetX() - vertecies[0].X(),position.GetY() - vertecies[0].Y(),0);
    
    glScalef(scale.GetX(),scale.GetY(),0);
	
    color.Apply( );
    texture.Apply( );

    glBegin( GL_QUADS );
    for( int i = 0; i < 4; i++ )
    {
        textcoords[i].Apply( );
        vertecies[i].Apply( );
    }
    glEnd( );
    
    
    return true;
}

bool GraphicsCore::DrawLine(const Math::Vector2F& start,const Math::Vector2F& end,const gl::Color& color)
{
    //revert view matrix
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glBindTexture(GL_TEXTURE_2D,0);
    
    color.Apply( );
    
    gl::Vertex vertecies[2];
    
    vertecies[0].Set(start.GetX(),start.GetY());
    vertecies[1].Set(end.GetX(),end.GetY());
    
    glBegin( GL_LINE );
    for( int i = 0; i < 2; i++ )
    {
        vertecies[i].Apply( );
    }
    glEnd( );

    return true;
}
    
bool GraphicsCore::DrawRect(const Math::Vector2F& start,const Math::Vector2F& end,const gl::Color& color)
{
    //revert view matrix
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glBindTexture(GL_TEXTURE_2D,0);
    color.Apply( );
    
    gl::Vertex vertecies[4];
    
    vertecies[0].Set(start.GetX(),start.GetY());
    vertecies[2].Set(end.GetX(),end.GetY());
    vertecies[1].Set(end.GetX(),start.GetY());
    vertecies[3].Set(start.GetX(),end.GetY());
    
    glBegin( GL_POLYGON );
    for( int i = 0; i < 4; i++ )
    {
        vertecies[i].Apply( );
    }
    glEnd( );

    return true;
}

bool GraphicsCore::BeginRendering()
{
    mainScreen->Clear( );
    return true;
}

bool GraphicsCore::EndRendering()
{
    sdlScreen->Render( );
    mainScreen->Flip( );
    sdlScreen->FillTransparent( );
    glFlush( );
    glFinish( );
    return true;
}

void GraphicsCore::CreateExtensionList()
{
    //get all available extensions and check if the specified exists
    char* ext = ( char* ) ( glGetString( GL_EXTENSIONS ) );
    const char* item = strtok(ext," ");
    while(item != NULL)
    {
        extensionList.push_back(item);
        item = strtok(NULL," ");
    }
    extensionList.sort();
}

bool GraphicsCore::CheckForExtension(const char* name)
{
    return std::binary_search(extensionList.begin(),extensionList.end(),name);
}

Canvas* GraphicsCore::GetSdlScreen() const
{
    return sdlScreen;
}

Screen* GraphicsCore::GetGlScreen() const
{
    return mainScreen;
}

void GraphicsCore::TextureCoordinates(gl::TextureCoords tCoords[4], const gl::Texture& texture, const Math::Rect& clip, bool flip[2]) const
{
    float w = texture.GetWidth( );
    float h = texture.GetHeight( );

    float x1 = clip.GetLeft( ) / w;
    float x2 = clip.GetWidth( ) / w + x1;
    float y1 = clip.GetTop( ) / h;
    float y2 = clip.GetHeight( ) / h + y1;

    if( !flip[0] )
    {
        tCoords[0].SetX( x1 );
        tCoords[2].SetX( x2 );
    }
    else
    {
        tCoords[0].SetX( x2 );
        tCoords[2].SetX( x1 );
    }

    if( !flip[1] )
    {
        tCoords[0].SetY( y1 );
        tCoords[2].SetY( y2 );
    }
    else
    {
        tCoords[0].SetY( y2 );
        tCoords[2].SetY( y1 );
    }

    tCoords[1].Set( tCoords[2].X( ), tCoords[0].Y( ) );

    tCoords[3].Set( tCoords[0].X( ), tCoords[2].Y( ) );
}

void GraphicsCore::Transorm(gl::Vertex vertecies[4], const Math::Vector2F& position, const Math::Rect& clip, const Math::Vector2F& scale) const
{
	//divide the two dimensions by two.(it is achieved by shifting one time to the left)
    float halfWidth = clip.GetWidth( ) >> 1;
    float halfHeight = clip.GetHeight( ) >> 1;
    vertecies[0].Set( -halfWidth , -halfHeight );
    vertecies[2].Set( halfWidth , halfHeight );
    vertecies[1].Set( vertecies[2].X( ), vertecies[0].Y( ) );
    vertecies[3].Set( vertecies[0].X( ), vertecies[2].Y( ) );
}

void GraphicsCore::SetOpenglConstants()
{
    /*
     * Set up the attributes of video.
     */
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_BUFFER_SIZE, 32 );

    SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ACCUM_ALPHA_SIZE, 8 );

    SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
    SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 2 );

    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
}

}
